package dbmanager

import (
	"database/sql"
	"database/sql/driver"
	"encoding/binary"
	"math"
	"reflect"
	"time"

	"gitee.com/night-tc/gobige/common"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/msgdef/protomsg"
	"gorm.io/gorm"
)

// DataArgs.go 文件定义了数据管理相关的核心结构体和功能。
// 主要包括：
// 1. DataArgs：用于管理数据更新和保存的上下文，支持全量更新和部分更新。
// 2. ValueMD：用于缓存数据模型及其反射值。
// 3. MapModel：用于部分字段更新的数据模型。
// 4. 提供了数据加载、更新、删除、撤销删除等操作的功能。

var (
	ErrPlayer     = common.NewError(-1, " PlayerID == 0")                      // 玩家ID为0时的错误
	ErrID         = common.NewError(-1, " ID == 0")                            // ID为0时的错误
	ErrPartial    = common.NewError(-1, " SetUpdate fail GetIsPartial() fail") // 部分更新失败时的错误
	ErrAllMD      = common.NewError(-1, " SetUpdate fail is All data")         // 全量更新失败时的错误
	ErrNoData     = common.NewError(-1, " Load redis no data")                 // 从Redis加载数据失败时的错误
	ErrNoLoadData = common.NewError(-1, " Load data not exist.keyname:%s")     // 缓存数据不存在时的错误
)

// NewDataArgs 创建一个新的DataArgs实例
func NewDataArgs(pid uint64) *DataArgs {
	return &DataArgs{
		pid:      pid,
		dblist:   make(map[string]IDataBaseModel),
		leadlist: make(map[string]*ValueMD),
	}
}

// DataArgs 用于管理数据更新和保存的上下文
type DataArgs struct {
	pid      uint64                    // 玩家ID
	leadlist map[string]*ValueMD       // 缓存的数据模型
	dblist   map[string]IDataBaseModel // 变化的数据模型
	isupdate bool                      // 是否有数据更新
	saveF    func()                    // 委托保存函数
}

// SetSaveF 设置委托保存函数
func (args *DataArgs) SetSaveF(f func()) {
	args.saveF = f
}

// setdblist 将数据模型添加到dblist中，并标记为已更新
func (args *DataArgs) setdblist(md IDataBaseModel) {
	args.dblist[md.KeyName()] = md
	if !args.isupdate && args.saveF != nil {
		args.saveF()
	}
	args.isupdate = true
}

// GetIsDirty 判断是否有脏数据
func (args *DataArgs) GetIsDirty() bool {
	return args.isupdate
}

// String 返回DataArgs的字符串表示（当前为空）
func (args *DataArgs) String() string {
	return ""
}

// GetDirtyData 获取所有脏数据
func (args *DataArgs) GetDirtyData() map[string]IDataBaseModel {
	return args.dblist
}

// Clear 清空数据并重置状态
func (args *DataArgs) Clear() {
	// 删除数据，还需要把缓存的对象也删除
	for k, md := range args.dblist {
		if md.IsDeleted() {
			delete(args.leadlist, k)
		}
	}
	args.dblist = make(map[string]IDataBaseModel)
	args.isupdate = false
}

// GetPID 获取玩家ID
func (args *DataArgs) GetPID() uint64 {
	return args.pid
}

// LoadData 加载需要支持差量更新的数据
func (args *DataArgs) LoadData(md IDataBaseModel) {
	v := new(ValueMD)
	v.vElem = reflect.ValueOf(md).Elem()
	v.Model = md
	args.leadlist[md.KeyName()] = v
	if d, ok := md.(ISetArgs); ok {
		d.SetArgs(args)
	}
}

// GetModelByKeyName 使用Keyname获取数据对象
func (args *DataArgs) GetModelByKeyName(keyname string) IDataBaseModel {
	v, ok := args.leadlist[keyname]
	if ok {
		return v.Model
	}
	return nil
}

// Update 全量更新数据模型
func (args *DataArgs) Update(md IDataBaseModel) error {
	if err := CheckModel(md); err != nil {
		return err
	}
	if _, ok := args.leadlist[md.KeyName()]; !ok {
		args.LoadData(md)
	}

	// 进行全量更新的时候，不再进行差量设置
	args.setdblist(md)
	return nil
}

// Delete 标记数据模型为已删除
func (args *DataArgs) Delete(md IDataBaseModel) {
	md.SetDelete()
	if md.GetIsPartial() {
		if data, ok := args.dblist[md.KeyName()]; !ok {
			mapmd := NewMapModel(md.KeyName(), md.TableName())
			mapmd.SetPlayerID(md.GetPID())
			mapmd.SetDelete()
			if sub, ok := md.(IPlayerSubBase); ok {
				mapmd.SetID(sub.GetID())
			}
			args.setdblist(mapmd)
		} else if mapmd, ok := data.(*MapModel); ok {
			mapmd.SetDelete()
		}
	} else {
		args.setdblist(md)
	}
}

// RevokeDelete 撤销删除操作
func (args *DataArgs) RevokeDelete(md IDataBaseModel) {
	md.RevokeDelete()
	if md.GetIsPartial() {
		if data, ok := args.dblist[md.KeyName()]; ok {
			data.RevokeDelete()
		}
	} else {
		args.setdblist(md)
	}
}

// SetUpdate 设置脏字段，进行部分Field更新
func (args *DataArgs) SetUpdate(md IDataBaseModel, k string, v interface{}) error {
	if err := CheckModel(md); err != nil {
		return err
	}
	if md.GetIsPartial() {
		if data, ok := args.dblist[md.KeyName()]; !ok {
			mapmd := NewMapModel(md.KeyName(), md.TableName())
			mapmd.SetPlayerID(md.GetPID())
			mapmd.SetData(k, reflect.ValueOf(v))
			if sub, ok := md.(IPlayerSubBase); ok {
				mapmd.SetID(sub.GetID())
			}
			args.setdblist(mapmd)
		} else if mapmd, ok := data.(*MapModel); ok {
			mapmd.SetData(k, reflect.ValueOf(v))
		} else {
			return ErrAllMD
		}
	} else {
		return ErrPartial
	}
	return nil
}

// UpdateField 使用字段名更新数据
func (args *DataArgs) UpdateField(keyname, field string) error {
	vmd, ok := args.leadlist[keyname]
	if !ok {
		panic("Not KeyName: " + keyname)
	}
	v := vmd.vElem.FieldByName(field)
	if vmd.Model.GetIsPartial() {
		if data, ok := args.dblist[keyname]; !ok {
			mapmd := NewMapModel(keyname, vmd.Model.TableName())
			mapmd.SetPlayerID(vmd.Model.GetPID())
			mapmd.SetData(field, v)
			if sub, ok := vmd.Model.(IPlayerSubBase); ok {
				mapmd.SetID(sub.GetID())
			}
			args.setdblist(mapmd)
		} else if mapmd, ok := data.(*MapModel); ok {
			mapmd.SetData(field, v)
		} else {
			return ErrAllMD
		}
	} else {
		return ErrPartial
	}
	return nil
}

// GetMsgByForFunc 返回发给DB服的消息，支持遍历时自定义处理函数
func (args *DataArgs) GetMsgByForFunc(f func(v IDataBaseModel, db *protomsg.DBDataModel) (issave bool)) (result *protomsg.DBUpUserDatasReq) {
	result = new(protomsg.DBUpUserDatasReq)
	result.PID = args.pid
	result.DelList = make([]*protomsg.DBDataModel, 0, len(args.dblist))
	result.DBList = make([]*protomsg.DBDataModel, 0, len(args.dblist))

	for _, v := range args.dblist {
		db := new(protomsg.DBDataModel)
		db.KeyName = v.KeyName()
		db.MsgContent, _ = v.Marshal()
		if _, ok := v.(*MapModel); ok {
			db.IsPartial = true
		}
		issave := true
		if f != nil {
			issave = f(v, db)
		}
		if !issave {
			continue
		}
		if v.IsDeleted() {
			result.DelList = append(result.DelList, db)
		} else {
			result.DBList = append(result.DBList, db)
		}
	}

	return result
}

// GetMsgByAllData 获取全量更新数据包
func (args *DataArgs) GetMsgByAllData(f func(v IDataBaseModel, db *protomsg.DBDataModel) (issync bool)) (result *protomsg.DBUpUserDatasReq) {
	result = new(protomsg.DBUpUserDatasReq)
	result.PID = args.pid
	result.DelList = make([]*protomsg.DBDataModel, 0, len(args.dblist))
	result.DBList = make([]*protomsg.DBDataModel, 0, len(args.dblist))
	for _, v := range args.leadlist {
		db := new(protomsg.DBDataModel)
		db.KeyName = v.Model.KeyName()
		db.MsgContent, _ = v.Model.Marshal()
		issync := true
		if f != nil {
			issync = f(v.Model, db)
		}
		if !issync {
			continue
		}
		result.DBList = append(result.DBList, db)
	}

	return result
}

// GetRedisSave 获取需要保存到Redis的数据
func (args *DataArgs) GetRedisSave() (li map[string]IDataBaseModel, dbdatali []*protomsg.DBDataModel) {
	li = make(map[string]IDataBaseModel)
	dbdatali = make([]*protomsg.DBDataModel, 0, 10)
	for k, v := range args.leadlist {
		if v.Model.GetIsPartial() {
			li[k] = v.Model
		} else {
			db := new(protomsg.DBDataModel)
			db.KeyName = v.Model.KeyName()
			db.MsgContent, _ = v.Model.Marshal()
			dbdatali = append(dbdatali, db)
		}
	}
	return
}

// ValueMD 用于缓存数据模型及其反射值
type ValueMD struct {
	vElem reflect.Value
	Model IDataBaseModel
}

// NewMapModel 创建一个新的MapModel实例
func NewMapModel(keyname, table string) (result *MapModel) {
	result = &MapModel{
		keyName: keyname,
		Data:    map[string]reflect.Value{},
		Table:   table,
	}
	return
}

// MapModel 用于部分字段更新的数据模型
type MapModel struct {
	Data      map[string]reflect.Value
	keyName   string
	Table     string
	DeletedAt gorm.DeletedAt `gorm:"index"`
}

// KeyName 返回数据的唯一名字
func (this *MapModel) KeyName() string {
	return this.keyName
}

// GetPID 获取用户主键
func (this *MapModel) GetPID() uint64 {
	return this.Data["PID"].Uint()
}

// SetPlayerID 设置用户主键
func (this *MapModel) SetPlayerID(v uint64) {
	this.Data["PID"] = reflect.ValueOf(v)
}

// TableName 返回表名
func (this *MapModel) TableName() string {
	return this.Table
}

// Marshal 序列化数据
func (this *MapModel) Marshal() ([]byte, error) {
	md := new(protomsg.TableData)
	md.Datas = make(map[string][]byte)
	for k, v := range this.Data {
		if d := PackMsgData(v); d != nil {
			md.Datas[k] = d
		}
	}
	return md.Marshal()
}

// Unmarshal 反序列化数据
func (this *MapModel) Unmarshal(data []byte) error {
	return nil
}

// GetIsPartial 判断是否支持部分更新
func (this *MapModel) GetIsPartial() bool {
	return true
}

// IsDeleted 判断数据是否被删除
func (this *MapModel) IsDeleted() bool {
	return this.DeletedAt.Valid && (this.DeletedAt.Time != time.Time{})
}

// SetDelete 标记数据为已删除
func (this *MapModel) SetDelete() {
	this.DeletedAt.Time = time.Now()
	this.DeletedAt.Valid = true
}

// RevokeDelete 撤销删除标记
func (this *MapModel) RevokeDelete() {
	this.DeletedAt = gorm.DeletedAt{}
}

// SetData 设置字段数据
func (this *MapModel) SetData(k string, v reflect.Value) {
	this.Data[k] = v
}

// GetID 获取主键ID
func (this *MapModel) GetID() uint64 {
	return this.Data["ID"].Uint()
}

// SetID 设置主键ID
func (this *MapModel) SetID(id uint64) {
	this.Data["ID"] = reflect.ValueOf(id)
}

// DBResolveModel 将数据模型分解为字段数据
func DBResolveModel(md IDataBaseModel) (result map[string]reflect.Value) {
	result = make(map[string]reflect.Value)
	if mapmd, ok := md.(*MapModel); ok {
		result = mapmd.Data
	} else {
		if smd, ok := md.(IReal); ok {
			md = smd.GetMDReal()
		}
		mdv := reflect.ValueOf(md).Elem()
		mdt := mdv.Type()
		for i := 0; i < mdv.NumField(); i++ {
			dbResolveModel(result, mdt.Field(i), mdv.Field(i))
		}
	}

	return
}

// dbResolveModel 递归解析数据模型字段
func dbResolveModel(li map[string]reflect.Value, f reflect.StructField, data reflect.Value) {
	if tagv, ok := f.Tag.Lookup("protobuf"); !ok || tagv == "-" {
		return
	}

	datat := data.Type()
	switch datat.Kind() {
	case reflect.Struct:
		if _, ok3 := data.Interface().(driver.Valuer); ok3 {
			li[f.Name] = data
		} else if _, ok2 := data.Interface().(time.Time); ok2 {
			li[f.Name] = data
		} else {
			for i := 0; i < data.NumField(); i++ {
				dbResolveModel(li, datat.Field(i), data.Field(i))
			}
		}
	default:
		li[f.Name] = data
	}
}

// CheckModel 检查数据模型的有效性
func CheckModel(md IDataBaseModel) error {
	if md.GetPID() == 0 {
		panic(ErrPlayer)
	} else if sub, ok := md.(IPlayerSubBase); ok && sub.GetID() == 0 {
		panic(ErrID)
	}
	return nil
}

// PackMsgData 打包字段数据
func PackMsgData(v reflect.Value) (result []byte) {
	result = make([]byte, 0)
	switch v.Kind() {
	case reflect.Struct:
		if vf, ok := v.Interface().(driver.Valuer); ok {
			buf, _ := vf.Value()
			if buf == nil {
				return nil
			}
			result = buf.([]byte)
		} else {
			return nil
		}
	case reflect.Ptr:
		if vf, ok := v.Elem().Interface().(driver.Valuer); ok {
			buf, _ := vf.Value()
			if buf == nil {
				return nil
			}
			result = buf.([]byte)
		} else {
			return nil
		}
	case reflect.Float32, reflect.Float64:
		result = binary.AppendUvarint(result, math.Float64bits(v.Float()))
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		result = binary.AppendVarint(result, v.Int())
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		result = binary.AppendUvarint(result, v.Uint())
	case reflect.String:
		result = []byte(v.String())
	case reflect.Bool:
		if v.Bool() {
			result = append(result, byte(1))
		} else {
			result = append(result, byte(0))
		}
	case reflect.Slice:
		if v.Type().Elem().Kind() == reflect.Uint8 {
			result = v.Bytes()
		}
	default:
		return nil
	}

	return
}

// UnPackMsgDataToField 从消息中解包数据到字段
func UnPackMsgDataToField(f reflect.Value, msgdata []byte) {
	switch f.Kind() {
	case reflect.Struct:
		if v, ok := f.Addr().Interface().(sql.Scanner); ok {
			_ = v.Scan(msgdata)
		}
	case reflect.Ptr:
		if v, ok := f.Interface().(sql.Scanner); ok {
			_ = v.Scan(msgdata)
		}
	case reflect.Float32:
		v, _ := binary.Uvarint(msgdata)
		f.SetFloat(float64(math.Float32frombits(uint32(v))))
	case reflect.Float64:
		v, _ := binary.Uvarint(msgdata)
		f.SetFloat(float64(math.Float64frombits(uint64(v))))
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		v, _ := binary.Varint(msgdata)
		f.SetInt(v)
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		v, _ := binary.Uvarint(msgdata)
		f.SetUint(v)
	case reflect.String:
		f.SetString(string(msgdata))
	case reflect.Slice:
		if f.Type().Elem().Kind() == reflect.Uint8 {
			f.SetBytes(msgdata)
		}
	case reflect.Bool:
		f.SetBool(msgdata[0] == 1)
	default:
	}
}

// SetDataModelByMsg 从消息中设置数据差量
func SetDataModelByMsg(md IDataBaseModel, msg *protomsg.DBDataModel) {
	msgdata := new(protomsg.TableData)
	if err := msgdata.Unmarshal(msg.MsgContent); err != nil {
		logger.Warnf("SetDataModelByMsg md:%s msg:%s", md, msg)
	}
	SetDataModel(md, msgdata)
}

// SetDataModel 设置数据到差量中去
func SetDataModel(md IDataBaseModel, msgdata *protomsg.TableData) {
	defer func() {
		if err := recover(); err != nil {
			logger.Errorf("SetDataModelByMsg panic:%v table:%s PlayerID:%d data:%v",
				err, md.TableName(), md.GetPID(), msgdata.Datas)
		}
	}()
	vmd := reflect.ValueOf(md).Elem()
	for k, v := range msgdata.Datas {
		f := vmd.FieldByName(k)
		UnPackMsgDataToField(f, v)
	}
}

// SetDataModel2ByMsg 从消息中设置数据差量，包含标脏
func SetDataModel2ByMsg(md IDataBaseModel, msg *protomsg.DBDataModel, args *DataArgs) {
	msgdata := new(protomsg.TableData)
	if err := msgdata.Unmarshal(msg.MsgContent); err != nil {
		logger.Warnf("SetDataModelByMsg md:%s msg:%s", md, msg)
	}
	SetDataModel2(md, msgdata, args)
}

// SetDataModel2 设置数据到差量中去，包含标脏
func SetDataModel2(md IDataBaseModel, msgdata *protomsg.TableData, args *DataArgs) {
	vmd := reflect.ValueOf(md).Elem()
	for k, v := range msgdata.Datas {
		f := vmd.FieldByName(k)
		UnPackMsgDataToField(f, v)
		_ = args.UpdateField(md.KeyName(), k)
	}
}
